package com.dcrain.api.authz.common;

import java.io.IOException;
import java.util.Map;

import javax.servlet.Filter;
import javax.servlet.FilterChain;
import javax.servlet.FilterConfig;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.alibaba.fastjson.JSONObject;
import com.alibaba.fastjson.parser.Feature;
import com.dcrain.api.authz.common.utils.HttpHelper;
import com.dcrain.api.authz.common.utils.RequestUtils;
import com.dcrain.api.authz.common.utils.SignatureUtils;

public class ApiAuthFilter implements Filter {
	private final Logger logger = LoggerFactory.getLogger(ApiAuthFilter.class);
	private static String[] auParams;
	private static String[] excludedPathArray;
	private static String signKey="";

	public void init(FilterConfig filterConfig) throws ServletException {
		String authParams = filterConfig.getInitParameter(ApiConstant.AUTHHEADERPARAMS);
		if (authParams != null && authParams.trim().length() > 0) {
			auParams = authParams.split(",");
		}else {
			auParams = ApiConstant.validate_params;
		}
		
		String excluded = filterConfig.getInitParameter(ApiConstant.excludedPaths);
		if (excluded != null && excluded.trim().length() > 0) {
			excludedPathArray = excluded.split(",");
		}
		
		String skey = filterConfig.getInitParameter(ApiConstant.sign_key);
		if (skey != null && skey.trim().length() > 0) {
			signKey = skey;
		}
	}

	@SuppressWarnings("unchecked")
	public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain chain)
			throws IOException, ServletException {
		final long start = System.currentTimeMillis();
		HttpServletRequest request = (HttpServletRequest) servletRequest;
		HttpServletResponse response = (HttpServletResponse) servletResponse;
		logger.debug("path:"+request.getServletPath());
		// validate url
		if(excludedPathArray!=null) {
			for (String excludedPath : excludedPathArray) {
				if(excludedPath.equals(request.getServletPath())){   
					logger.debug("excludedPath="+excludedPath+" return.");
					chain.doFilter(servletRequest, servletResponse);   
					return;
				}
		    }
		}
		
		// validate content_type
		if (!ApiConstant.CONTENT_TYPE.equalsIgnoreCase(servletRequest.getContentType())) {
			logger.error("Content-Type="+servletRequest.getContentType()+" 不支持 return. ");
			RequestUtils.backResponseJson(response, new R().error(ApiCodeEnum.CONTENT_TYPE));
			return;
		} 
		
		ServletRequest requestWrapper = null;
		// validate header params
		if(auParams!=null) {
			for (String param : auParams) {
				String headerValue = request.getHeader(param);
				logger.info("header:"+param+"="+headerValue);
				if (headerValue == null || headerValue.trim().length() < 1) { 
					logger.error("param="+param+" 未取到值 return. ");
					RequestUtils.backResponseJson(response, new R().error(ApiCodeEnum.PARAMETER_ERROR,"header("+param+")值为空"));
					return;
				}
				
				if(ApiConstant.TIMESTAMP.equalsIgnoreCase(param)) {
					long reqeustInterval = System.currentTimeMillis() - Long.valueOf(headerValue);
					if (reqeustInterval >= 5 * 60 * 1000) {//5分钟
						logger.error("TIMESTAMP_TIMEOUT System.currentTimeMillis()-->" + System.currentTimeMillis() + " + 300000= "+ (System.currentTimeMillis() + 300000));
						RequestUtils.backResponseJson(response, new R().error(ApiCodeEnum.TIMESTAMP_TIMEOUT));
						return;
					}
				}
				
				if(ApiConstant.SIGN.equalsIgnoreCase(param)) {
					//validate A->get 参数方式请求
					final Map reqMap = RequestUtils.getParameterMap(request);
					if(reqMap.keySet().size()>0) {
						if(!SignatureUtils.validateSign(reqMap, request.getHeader(ApiConstant.NONCE), signKey, request.getHeader(ApiConstant.SIGN))) {
							logger.error("sign验证失败.");
							RequestUtils.backResponseJson(response, new R().error(ApiCodeEnum.SIGN_ERROR));
							return;
						}
					}
					//validate B->post bodyjson方式请求
					requestWrapper = new RequestReaderHttpServletRequestWrapper(request);
					final String body = HttpHelper.getBodyString(requestWrapper);
					logger.info(body);
					if(body!=null && body.trim().length()>1) {
						if(isJsonObject(body)) {//验证body能否转换为jsonobject
							Map<String, Object> map = JSONObject.parseObject(body, Feature.OrderedField);
							if(!SignatureUtils.validateSign(map, request.getHeader(ApiConstant.NONCE), signKey, request.getHeader(ApiConstant.SIGN))) {
								logger.error("sign验证失败.USED TIME IS:"+(System.currentTimeMillis()-start));
								RequestUtils.backResponseJson(response, new R().error(ApiCodeEnum.SIGN_ERROR));
								logger.info("USED TIME IS:"+(System.currentTimeMillis()-start));
								return;
							}
						}else {
							logger.error("body:"+body+" 无法转换为json格式.");
							RequestUtils.backResponseJson(response, new R().error(ApiCodeEnum.DATA_FORMAT));
							return;
						}
					}
				}
		    }
		}
		
		if(requestWrapper == null) {
            chain.doFilter(servletRequest, servletResponse);
        } else {
            chain.doFilter(requestWrapper, servletResponse);
        }
	}

	public void destroy() {
	}
	
	private static boolean isJsonObject(String content) {
	    // 此处应该注意，不要使用StringUtils.isEmpty(),因为当content为"  "空格字符串时，JSONObject.parseObject可以解析成功，
	    // 实际上，这是没有什么意义的。所以content应该是非空白字符串且不为空，判断是否是JSON数组也是相同的情况。
	    if(content==null || content.trim().length()<1)
	        return false;
	    try {
	        JSONObject.parseObject(content);
	        return true;
	    } catch (Exception e) {
	        return false;
	    }
	}
}
